import {Box2,Box3,BufferGeometry,Vector2, Vector3} from "three/src/Three";
import Const from "../../application/constants/Const";
import { Point } from "../../renderExtension/types/Point";
import GeoUtils from "../../utils/GeoUtils";
import {LineGeometry} from "three/examples/jsm/lines/LineGeometry";
import { HtmlMarker } from "../../Docs";
import GeometryUtil from "../../renderExtension/utils/GeometryUtil";
import WidthLineGeometry from "../../renderExtension/geometriy/WidthLineGeometry";

/**
* 面缓冲区分析
*/
export default class PolygonBufferAnalysis {
    public pointArray: Array<Point>;//原始Point点信息
    /**
     * 基于Polyline，每条边构成一个多边形，最终生成多个闭合多边形 
     */
    private multiSegmentPolygon;    

    /**
     * 由Polyline，最终生成多个闭合多边形，包围盒值 
     */
    private multiPolygonBox:Box2;

     /**
     * 由Polyline，最终生成多个闭合多边形，单个多边形包围盒值 
     */
    private singlePolygonBox:Array<Box2>;

    /**
     * 缓冲区分析对象数据
     */
    private rangeArray = [];

     /**
     * 缓冲区分析，缓冲范围参数
     */
    public bufferDistance;

     /**
     * 面缓冲分析后，结果
     */
    // public lineFilter = {};

    public constructor(cfg: any) {
        this.pointArray = cfg.pointArray;   

        let self = this;
        cfg.objects.forEach(element => {
            let range:{ minX: number, minY: number, maxX: number, maxY: number } = { minX: 0, minY: 0, maxX: 0, maxY: 0 };

            if(element instanceof HtmlMarker){
                range.minX = element.position.x;
                range.minY = element.position.y;
                range.maxX = element.position.x;
                range.maxY = element.position.y;
            }else{
                var box = new Box3();
                box.setFromObject(element);
                
                range.minX = box.min.x;
                range.minY = box.min.y;
                range.maxX = box.max.x;
                range.maxY = box.max.y;
            }

            self.rangeArray.push(range);
        });

        this.bufferDistance = (cfg.bufferDistance === undefined)?100:cfg.bufferDistance;
    }

     /**
     * 获取原始Point点信息
     */
    public GetPointArray() {
        return this.pointArray;    
     }

     /**
     * 获取多个闭合多边形。基于Polyline,如何两点，首、尾两端外延出各一个多边形，同时两点也构成一个多边形,最终生成多个闭合多边形
     */
    public GetMultiSegmentPolygon() {
        return this.multiSegmentPolygon;    
    }

     /**
     * 获取缓冲区外延后新构成的多边形
     */
    public SetOutDistance( bufferDistance:number ) {
        this.bufferDistance = bufferDistance;
    }

    public bufferAnalysis(){  
        this.generateBufferMesh();
        return this.insideLineFilter(this.rangeArray);
    }

    protected toAssign() {
        this.multiSegmentPolygon = [];
    }

     /** 
     * 基于线，构建线外延缓冲区域，如何线段首尾外延一段
     */
     private generateBufferMesh() { //生成缓冲区mesh      
        this.multiSegmentPolygon = [];
        let offsetValue = this.bufferDistance;
        let pointArrayBuffer: Array<Point>;//基于当前两个点，首、尾各拓展1个点，进行缓冲延伸
        let currentPoint;
        let nextPoint;  
        for (let i = 0; i < this.pointArray.length - 1; i++) {
            pointArrayBuffer = new Array<Point>();
            pointArrayBuffer = [];
            let firstLineCosSin = GeometryUtil.computeCosSin(this.pointArray[i], this.pointArray[i+1]);
            let newStartPoint = GeometryUtil.getNewPoint(this.pointArray[i], this.pointArray[i+1], offsetValue, firstLineCosSin);
    
            let endLineCosSin = GeometryUtil.computeCosSin(this.pointArray[i+1], this.pointArray[i]);
            let newEndPoint = GeometryUtil.getNewPoint(this.pointArray[i+1], this.pointArray[i], offsetValue, endLineCosSin);

            //基于当前两点，首尾各拓展1个点，进行延伸
            pointArrayBuffer.push(new Point(newStartPoint.x,newStartPoint.y,newStartPoint.z,Const.EPSGType.EPSGWeb));
            pointArrayBuffer.push(this.pointArray[i]);
            pointArrayBuffer.push(this.pointArray[i+1]);
            pointArrayBuffer.push(new Point(newEndPoint.x,newEndPoint.y,newEndPoint.z,Const.EPSGType.EPSGWeb));

            for (let j = 0; j < pointArrayBuffer.length -1; j++) {
                currentPoint = pointArrayBuffer[j];
                nextPoint = pointArrayBuffer[j + 1];

                let geometry: BufferGeometry = new WidthLineGeometry(currentPoint.clone(), nextPoint.clone(), offsetValue, currentPoint.clone());
                let pointArray: Array<Vector2> = geometry['pointArray'];
                let planePoints: Array<Point> = new Array<Point>();
                pointArray.forEach(element => {               
                    planePoints.push(new Point(element.x,element.y,0,Const.EPSGType.EPSGWeb));
                });
                planePoints.forEach(element => {
                    element.add(currentPoint);
                });             
                this.multiSegmentPolygon.push(planePoints);
            }  
        }

         //添加原始多边形区域
         let planePoints: Array<Point> = new Array<Point>();
         this.pointArray.forEach(element => {               
             planePoints.push(new Point(element.x,element.y,0,Const.EPSGType.EPSGWeb));
         });          
         this.multiSegmentPolygon.push(planePoints);
    }

    
     /** 
     * 基于线构建缓冲区域，对符合该区域范围内的对象进行过滤
     * @param objects 对象信息，含名称，位置及范围信息（如POI等点信息，minX=maxX，minY=maxY；对于模型新，为模型包围盒值）
     */
     private calcBoxInfo( ){
        this.multiPolygonBox = new Box2(); 
        this.singlePolygonBox = new Array<Box2>();
        let cur:Box2;
        
        let polygon;
        for(let i=0; i<this.multiSegmentPolygon.length; i++){                    
            polygon = this.multiSegmentPolygon[i];
            cur = new Box2();
            for(let j=0;j<polygon.length;j++)
            {
                if(j ==0 )
                {
                    cur.min.x = polygon[j].x;
                    cur.max.x = polygon[j].x         
                    cur.min.y = polygon[j].y;
                    cur.max.y = polygon[j].y;
                }
                else{
                    cur.min.x = Math.min(cur.min.x,polygon[j].x);
                    cur.max.x = Math.max(cur.max.x,polygon[j].x);
                    cur.min.y = Math.min(cur.min.y,polygon[j].y);
                    cur.max.y = Math.max(cur.max.y,polygon[j].y);                    
                }   
            }
 
            //console.log(cur);
            this.singlePolygonBox.push(cur);  
            
            //
            if(i==0)
            {
                this.multiPolygonBox = cur;
            }
            else{
                this.multiPolygonBox.min.x = Math.min(this.multiPolygonBox.min.x,cur.min.x);
                this.multiPolygonBox.max.x = Math.max(this.multiPolygonBox.max.x,cur.max.x);
                this.multiPolygonBox.min.y = Math.min(this.multiPolygonBox.min.y,cur.min.y);
                this.multiPolygonBox.max.y = Math.max(this.multiPolygonBox.max.y,cur.max.y);
            }
        }
    }

     /** 
     * 基于线构建缓冲区域，对符合该区域范围内的对象进行过滤
     * @param rangeArray 对象信息，含名称，位置及范围信息（如POI等点信息，minX=maxX，minY=maxY；对于模型新，为模型包围盒值）
     */
       private insideLineFilter(rangeArray:Array<{ minX: number, minY: number, maxX: number, maxY: number }> ){
        let isContain;
        let result = [];

        //计算包围盒信息
        this.calcBoxInfo();

        if (Array.isArray(rangeArray)) {
            for (let i = 0; i < rangeArray.length; i++) {
                isContain = false;
                let polygon;
                let objectCenter = new Vector2((rangeArray[i].maxX + rangeArray[i].minX)/2, (rangeArray[i].maxY + rangeArray[i].minY)/2);
                
                //宏观判断，当前点是否在“多边形包围盒”里
                if( (objectCenter.x <= this.multiPolygonBox.max.x)&&(objectCenter.x >= this.multiPolygonBox.min.x)&&(objectCenter.y <= this.multiPolygonBox.max.y)&&(objectCenter.y >= this.multiPolygonBox.min.y) )
                {
                    for(let j=0; j<this.multiSegmentPolygon.length; j++){ 
                        if( (objectCenter.x <= this.singlePolygonBox[j].max.x)&&(objectCenter.x >= this.singlePolygonBox[j].min.x)&&(objectCenter.y <= this.singlePolygonBox[j].max.y)&&(objectCenter.y >= this.singlePolygonBox[j].min.y) )
                        {
                            polygon = this.multiSegmentPolygon[j];
                            if(GeometryUtil.isInShape(objectCenter, polygon)){
                                isContain = true;
                                break;
                            }                            
                        }
                    }
                }
                result[i] = isContain;
            }
        }     
        return result;
    }
}